#include "TSkinDialog.h"
#include <QMouseEvent>
#include <QKeyEvent>
#include <QApplication>
#include <QDesktopWidget>
#include <QVBoxLayout>
#include "TResizableFrame.h"

const int FRAME_WIDTH = 4;
const int RESZIE_WIDTH = FRAME_WIDTH;

TSkinDialog::TSkinDialog(QWidget *parent, Qt::WindowFlags f)
    : QDialog(parent, f | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint),
      _movable(true), _manualMoved(false), _resizeEnabled(false), _resizeFrame(HT_NONE), _lastHit(HT_NONE)
{
    //	setAttribute(Qt::WA_TranslucentBackground, true);
    setMinimumSize(80, 80);

    QVBoxLayout* verticalLayout = new QVBoxLayout(this);
    verticalLayout->setContentsMargins(0, 0, 0, 0);
    verticalLayout->setSpacing(0);

    _background = new QFrame(this);
    _background->setObjectName("popup_bg");
    _background->resize(size());
    _background->ensurePolished();
    verticalLayout->addWidget(_background);

    QVBoxLayout* verticalLayout2 = new QVBoxLayout(_background);
    verticalLayout2->setContentsMargins(0, 0, 0, 0);
    verticalLayout2->setSpacing(0);

    _titlebar = new TTitleBar("popup", _background);
    _titlebar->setObjectName("ForDialog");
    _titlebar->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
    _titlebar->ensurePolished();
    _titlebar->setFixedHeight(25);
    verticalLayout2->addWidget(_titlebar);
    _titlebar->setContentsMargins(0, 0, 3, 0);

    _closeButton = new QPushButton(_titlebar);
    _closeButton->setObjectName("popup_close");
    _closeButton->setToolTip(tr("close"));
    _closeButton->setFixedSize(24, 14);
    _closeButton->setCursor(QCursor(Qt::PointingHandCursor));
    _titlebar->addWidget(_closeButton);

    _client = new QFrame(_background);
    _client->setObjectName("popup_client");
    _client->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    verticalLayout2->addWidget(_client);

    connect(_closeButton, SIGNAL(clicked()), this, SLOT(close()));
    connect(_titlebar, SIGNAL(moveRequested(const QPoint&, const QPoint&)), this,
            SLOT(moveRequested(const QPoint&, const QPoint&)));
}

TSkinDialog::~TSkinDialog()
{
    _background = NULL;
    _closeButton = NULL;
    _titlebar = NULL;
    _client = NULL;
}

void TSkinDialog::setResizeEnabled(bool setting)
{
    if (_resizeEnabled != setting) {
        _resizeEnabled = (setting && !TResizableFrame::isFixedSize(this));
        if (setting)
            QApplication::instance()->installEventFilter(this);
        else
            QApplication::instance()->removeEventFilter(this);
    }
}

void TSkinDialog::changeEvent(QEvent* e)
{
    QDialog::changeEvent(e);
    if (e->type() == QEvent::WindowTitleChange)
        _titlebar->setText(windowTitle());
}

void TSkinDialog::showEvent(QShowEvent* e)
{
    QApplication::postEvent(this, new QEvent(QEvent::UpdateRequest), Qt::LowEventPriority);
    QDialog::showEvent(e);
}

void TSkinDialog::closeEvent(QCloseEvent* e)
{
    QDialog::closeEvent(e);
    changeCursor(HT_NONE);
    emit closed();
}

bool TSkinDialog::event(QEvent* e)
{
    if (e->type() == QEvent::KeyPress) {
        QKeyEvent* ke = static_cast<QKeyEvent*> (e);
        if (ke->key() == Qt::Key_Escape) {
            close();
            return true;
        }
    }
    return QDialog::event(e);
}

void TSkinDialog::moveRequested(const QPoint& globalPos, const QPoint& offset)
{
    if (isMovable()) {
        QPoint oldPos = pos();
        QPoint newPos = oldPos + offset;
        QPoint topLeft = adjuestPosition(newPos);
        if (topLeft != oldPos) {
            move(topLeft);
            _manualMoved = true;
        }
    }
}

void TSkinDialog::setMovementArea(const QRectF& area)
{
    if (_movementArea != area) {
        _movementArea = area;
        QPoint oldPos = pos();
        QPoint newPos = adjuestPosition(oldPos);
        if (oldPos != newPos)
            move(newPos);
    }
}

QPoint TSkinDialog::adjuestPosition(const QPoint& pt)
{
    QPoint topLeft = pt;
    if (!_movementArea.isValid())
        return topLeft;

    QSize sz;
    if (parentWidget())
        sz = parentWidget()->size();
    else
        sz = QApplication::desktop()->size();
    QRect area(_movementArea.left() * sz.width(), _movementArea.top() * sz.height(),
            _movementArea.width() * sz.width(), _movementArea.height() * sz.height());
    if (topLeft.x() < area.left())
        topLeft.setX(area.left());
    else if ((topLeft.x() + width()) > area.right())
        topLeft.setX(area.right() - width());
    if ((topLeft.y() + height()) > area.bottom())
        topLeft.setY(area.bottom() - height());
    if (topLeft.y() < area.top())
        topLeft.setY(area.top());

    return topLeft;
}

void TSkinDialog::centerWindow()
{
    QRect newRect = frameGeometry();
    int w = newRect.width();
    int h = newRect.height();
    int screenNumber = -1;
    QSize parentSize;
    QWidget* pw = parentWidget();
    if (pw) {
        screenNumber = QApplication::desktop()->screenNumber(pw);
        parentSize = pw->frameGeometry().size();
    } else {
        parentSize = QApplication::desktop()->screenGeometry(this).size();
    }
    int x = (parentSize.width() - newRect.width()) / 2;
    int y = (parentSize.height() - newRect.height()) / 2;
    QPoint topLeft(x, y);
    if (windowType() == Qt::SubWindow) {
        move(topLeft);
    } else {
        if (pw)
            topLeft = pw->mapToGlobal(topLeft);
        newRect.moveTopLeft(topLeft);
        QRect screenRect = QApplication::desktop()->screenGeometry(screenNumber);
        if (newRect.left() < screenRect.left()) {
            newRect.moveLeft(screenRect.left());
        } else if (newRect.right() > screenRect.right()) {
            x = screenRect.right() - w;
            newRect.moveLeft(x);
        }
        if (newRect.top() < screenRect.top())
            newRect.moveTop(screenRect.top());
        else if (newRect.bottom() > screenRect.bottom()) {
            newRect.moveTop(screenRect.bottom() - h);
        }
        move(newRect.topLeft());
    }
}

int TSkinDialog::hitTest(const QPoint& mpos) const
{
    QRect whole(0, 0, frameGeometry().width(), frameGeometry().height());
    QRect test;

    // LeftTop
    test.setRect(0, 0, RESZIE_WIDTH, RESZIE_WIDTH);
    if (test.contains(mpos))
        return HT_LEFTTOP;

    // RightTop
    test.setRect(whole.right() - RESZIE_WIDTH, 0, RESZIE_WIDTH, RESZIE_WIDTH);
    if (test.contains(mpos))
        return HT_RIGHTTOP;

    // LeftBottom
    test.setRect(0, whole.bottom() - RESZIE_WIDTH, RESZIE_WIDTH, RESZIE_WIDTH);
    if (test.contains(mpos))
        return HT_LEFTBOTTOM;

    // RightBottom
    test.setRect(whole.right() - RESZIE_WIDTH, test.bottom(), RESZIE_WIDTH, RESZIE_WIDTH);
    if (test.contains(mpos))
        return HT_RIGHTBOTTOM;

    // LeftFrame
    test.setRect(0, RESZIE_WIDTH, RESZIE_WIDTH, whole.height() - 2 * RESZIE_WIDTH);
    if (test.contains(mpos))
        return HT_LEFT;

    // TopFrame
    test.setRect(RESZIE_WIDTH, 0, whole.width() - 2 * RESZIE_WIDTH, RESZIE_WIDTH);
    if (test.contains(mpos))
        return HT_TOP;

    // RightFrame
    test.setRect(test.right(), RESZIE_WIDTH, RESZIE_WIDTH, whole.height() - 2 * RESZIE_WIDTH);
    if (test.contains(mpos))
        return HT_RIGHT;

    // BottomFrame
    test.setRect(RESZIE_WIDTH, test.bottom(), whole.width() - 2 * RESZIE_WIDTH, RESZIE_WIDTH);
    if (test.contains(mpos))
        return HT_BOTTOM;

    return HT_NONE;
}

void TSkinDialog::mousePressEvent(QMouseEvent* e)
{
    if (!_resizeEnabled || e->button() != Qt::LeftButton)
        return QDialog::mousePressEvent(e);

    e->accept();
    int ht = hitTest(e->pos());
    if (ht == HT_LEFT || ht == HT_TOP || ht == HT_RIGHT || ht == HT_BOTTOM || ht == HT_LEFTTOP || ht == HT_RIGHTTOP
            || ht == HT_LEFTBOTTOM || ht == HT_RIGHTBOTTOM) {
        _resizeFrame = ht;
        _resizeStartPosition = e->globalPos();
    }
}

void TSkinDialog::mouseReleaseEvent(QMouseEvent* e)
{
    e->accept();
    if (_resizeEnabled) {
        if (_resizeFrame != HT_NONE) {
            _resizeFrame = HT_NONE;
            _resizeStartPosition = QPoint();
        }
        int ht = hitTest(e->pos());
        if (_lastHit != ht)
            changeCursor(ht);
    }
}

bool TSkinDialog::eventFilter(QObject* obj, QEvent* e)
{
    if (!_resizeEnabled)
        return QObject::eventFilter(obj, e);

    if (e->type() == QEvent::MouseMove) {
        QMouseEvent* me = static_cast<QMouseEvent*> (e);
        if (me->buttons() == Qt::NoButton) {
            if (_resizeFrame == HT_NONE) {
                QPoint mpos = me->globalPos();
                if (isAncestorOf(QApplication::widgetAt(mpos))) {
                    mpos = mapFromGlobal(mpos);
                    int ht = hitTest(mpos);
                    if (_lastHit != ht)
                        changeCursor(ht);
                } else {
                    changeCursor(HT_NONE);
                }
            }
        } else if (me->buttons() == Qt::LeftButton) {
            if (_resizeFrame != HT_NONE)
                resizeFrame();
        }
    } else if (e->type() == QEvent::MouseButtonRelease) {
        if (_resizeFrame != HT_NONE) {
            _resizeFrame = HT_NONE;
            _resizeStartPosition = QPoint();
            changeCursor(HT_NONE);
        }
    }
    return false;
}

void TSkinDialog::changeCursor(int ht)
{
    if (_lastHit == HT_LEFT || _lastHit == HT_RIGHT || _lastHit == HT_TOP || _lastHit == HT_BOTTOM || _lastHit
            == HT_LEFTTOP || _lastHit == HT_RIGHTBOTTOM || _lastHit == HT_RIGHTTOP || _lastHit == HT_LEFTBOTTOM)
        QApplication::restoreOverrideCursor();

    switch (ht) {
    case HT_LEFT:
    case HT_RIGHT:
        QApplication::setOverrideCursor(Qt::SizeHorCursor);
        break;
    case HT_TOP:
    case HT_BOTTOM:
        QApplication::setOverrideCursor(Qt::SizeVerCursor);
        break;
    case HT_LEFTTOP:
    case HT_RIGHTBOTTOM:
        QApplication::setOverrideCursor(Qt::SizeFDiagCursor);
        break;
    case HT_RIGHTTOP:
    case HT_LEFTBOTTOM:
        QApplication::setOverrideCursor(Qt::SizeBDiagCursor);
        break;
    }

    _lastHit = ht;
}

void TSkinDialog::resizeFrame()
{
    QPoint cpos = QCursor::pos();
    QPoint offset = cpos - _resizeStartPosition;
    bool toLeft = (offset.x() < 0);
    bool toDown = (offset.y() > 0);
    QRect newRect = frameGeometry();
    QPoint leftLine(0, 0);
    QPoint topLine(0, 0);
    QPoint rightLine(newRect.width(), 0);
    QPoint bottomLine(0, newRect.height());
    switch (_resizeFrame) {
    case HT_LEFT:
        if (toLeft) {
            leftLine = mapToGlobal(leftLine);
            if (leftLine.x() < cpos.x()) {
                _resizeStartPosition = cpos;
                return;
            }
        }
        newRect.setLeft(newRect.left() + offset.x());
        break;
    case HT_TOP:
        if (!toDown) {
            topLine = mapToGlobal(topLine);
            if (topLine.y() < cpos.y()) {
                _resizeStartPosition = cpos;
                return;
            }
        }
        newRect.setTop(newRect.top() + offset.y());
        break;
    case HT_RIGHT:
        if (!toLeft) {
            rightLine = mapToGlobal(rightLine);
            if (rightLine.x() > cpos.x()) {
                _resizeStartPosition = cpos;
                return;
            }
        }
        newRect.setRight(newRect.right() + offset.x());
        break;
    case HT_BOTTOM:
        if (toDown) {
            bottomLine = mapToGlobal(bottomLine);
            if (bottomLine.y() > cpos.y()) {
                _resizeStartPosition = cpos;
                return;
            }
        }
        newRect.setBottom(newRect.bottom() + offset.y());
        break;
    case HT_LEFTTOP:
        if (toLeft) {
            leftLine = mapToGlobal(leftLine);
            if (leftLine.x() < cpos.x())
                offset.setX(0);
        }
        if (!toDown) {
            topLine = mapToGlobal(topLine);
            if (topLine.y() < cpos.y())
                offset.setY(0);
        }
        if (offset.x() == 0 && offset.y() == 0) {
            _resizeStartPosition = cpos;
            return;
        }
        newRect.setLeft(newRect.left() + offset.x());
        newRect.setTop(newRect.top() + offset.y());
        break;
    case HT_RIGHTTOP:
        if (!toLeft) {
            rightLine = mapToGlobal(rightLine);
            if (rightLine.x() > cpos.x())
                offset.setX(0);
        }
        if (!toDown) {
            topLine = mapToGlobal(topLine);
            if (topLine.y() < cpos.y())
                offset.setY(0);
        }
        if (offset.x() == 0 && offset.y() == 0) {
            _resizeStartPosition = cpos;
            return;
        }
        newRect.setRight(newRect.right() + offset.x());
        newRect.setTop(newRect.top() + offset.y());
        break;
    case HT_LEFTBOTTOM:
        if (toLeft) {
            leftLine = mapToGlobal(leftLine);
            if (leftLine.x() < cpos.x())
                offset.setX(0);
        }
        if (toDown) {
            bottomLine = mapToGlobal(bottomLine);
            if (bottomLine.y() > cpos.y())
                offset.setY(0);
        }
        if (offset.x() == 0 && offset.y() == 0) {
            _resizeStartPosition = cpos;
            return;
        }
        newRect.setLeft(newRect.left() + offset.x());
        newRect.setBottom(newRect.bottom() + offset.y());
        break;
    case HT_RIGHTBOTTOM:
        if (!toLeft) {
            rightLine = mapToGlobal(rightLine);
            if (rightLine.x() > cpos.x())
                offset.setX(0);
        }
        if (toDown) {
            bottomLine = mapToGlobal(bottomLine);
            if (bottomLine.y() > cpos.y())
                offset.setY(0);
        }
        if (offset.x() == 0 && offset.y() == 0) {
            _resizeStartPosition = cpos;
            return;
        }
        newRect.setRight(newRect.right() + offset.x());
        newRect.setBottom(newRect.bottom() + offset.y());
        break;
    }
    int left = newRect.left();
    int top = newRect.top();
    int right = newRect.right();
    int bottom = newRect.bottom();
    if (newRect.width() < minimumWidth()) {
        left = frameGeometry().left();
        right = frameGeometry().right();
    }
    if (newRect.height() < minimumHeight()) {
        top = frameGeometry().top();
        bottom = frameGeometry().bottom();
    }
    newRect.setCoords(left, top, right, bottom);
    setGeometry(newRect);
    _resizeStartPosition = cpos;
}
